package com.ibm.cps.newmessage;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.apache.log4j.Logger;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Throwables;
import com.ibm.cps.message.MessageFields;
import com.ibm.mongo.MongoPersistency;
import com.ibm.util.ErrorCode;
import com.ibm.util.JSONUtility;
import com.ibm.util.exception.CPSException;

public class DataSourceMetadataFactory {

	private static Map<String, Class<? extends AbstractDataSourceMetadata>> type2Constructor = new HashMap<String, Class<? extends AbstractDataSourceMetadata>>();
	private static Logger logger = Logger
			.getLogger(DataSourceMetadataFactory.class);

	private static HashSet<String> staticDataSouce = new HashSet<>();
	
	static {
		type2Constructor.put(MessageFields.MESSAGEQUEUE,
				MQDataSourceMetadata.class);
		type2Constructor.put(MessageFields.GRAPHITE,
				GraphiteDataSourceMetadata.class);
		type2Constructor.put(MessageFields.MONGO, MongoDataSourceMetaData.class);
		
		staticDataSouce.add(MessageFields.MONGO);
	}
	


	public static void registNewDataSource(String type,
			Class<? extends AbstractDataSourceMetadata> constructor)
			throws CPSException {
		if (type == null || constructor == null) {
			throw new CPSException(ErrorCode.INVALID_REQUEST_CODE,
					"Invaid type or contructor.");
		}
		type2Constructor.put(type, constructor);
	}

	public static AbstractDataSourceMetadata parseDataSourceMetadata(
			String jsonDataSource) throws CPSException {
		ObjectNode node = JSONUtility.fromObjectString(jsonDataSource);
		if (node.get(MessageFields.TYPE) == null) {
			throw new CPSException(ErrorCode.INVALID_INPUT_ERROR,
					"Data source type is null.");
		}
		return parseDataSourceMetadata(node, node.get(MessageFields.TYPE)
				.asText());
	}

	public static AbstractDataSourceMetadata parseDataSourceMetadata(
			String jsonDataSource, String type) throws CPSException {
		return parseDataSourceMetadata(
				JSONUtility.fromObjectString(jsonDataSource), type);
	}

	public static AbstractDataSourceMetadata parseDataSourceMetadata(
			ObjectNode dataSource, String type) throws CPSException {
		if (type == null) {
			throw new CPSException(ErrorCode.INVALID_INPUT_ERROR,
					"Data source type is null.");
		}
		if (type2Constructor.get(type) == null) {
			throw new CPSException(ErrorCode.INVALID_INPUT_ERROR,
					"Unknown data source type " + type);
		}
		Class<? extends AbstractDataSourceMetadata> dataSourceClass = type2Constructor
				.get(type);
		try {
			Constructor<? extends AbstractDataSourceMetadata> constructor = dataSourceClass
					.getConstructor(ObjectNode.class);
			return constructor.newInstance(dataSource);
		} catch (Throwable e) {
			if (e instanceof InvocationTargetException) {
				InvocationTargetException error = (InvocationTargetException) e;
				if (error.getTargetException() instanceof CPSException) {
					throw (CPSException) error.getTargetException();
				}
			}
			logger.error(Throwables.getStackTraceAsString(e));
			throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
					"Can't get datasource metadata instance successfully.");
		}

	}

	public static boolean isDataSourceType(String type) {
		return type2Constructor.keySet().contains(type);
	}
	
	public static boolean isStaticDataSource(String type){
		return staticDataSouce.contains(type);
	}

	public static Collection<String> loadExistedDataSource(String tenantid)
			throws CPSException {
		MongoPersistency persistency = MongoPersistency.getInstace();
		Collection<String> dataSources = persistency.getDataSource(tenantid,
				null);
		return dataSources;
	}


}
